/////////////////////////////////////////////////////////////////////////

package sheep.view;

/////////////////////////////////////////////////////////////////////////

import java.util.logging.Level;
import java.util.logging.Logger;
import sheep.model.graph.CyclicGraphException;
import sheep.view.Component.HyperlinkHandler;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import java.util.Stack;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;

import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.TimingTarget;
import org.jdesktop.fuse.InjectedResource;
import org.jdesktop.fuse.ResourceInjector;

/////////////////////////////////////////////////////////////////////////
import sheep.controller.Workspace;

// Barre de Navigation
import sheep.model.Task;
public class NavigationHeader extends JComponent {

  /////////////////////////////////////////////////////////////////////////

    // Gestionnaire de click sur la barre
    private final PathButtonHandler eventHandler;
    // Liste des bouttons, sous forme de pile vue le type de
    // navigation
    private final Stack<PathButton> buttonStack;
    // dernier boutton
    private String LastBttn;
    ////////////////////////////////////////////////////////////////////////////
    // Attributs initialisés dans le fichier ressource
    ////////////////////////////////////////////////////////////////////////////
    
    @InjectedResource
    private Color lightColor;
    @InjectedResource
    private Color shadowColor;
    @InjectedResource
    private int preferredHeight;
    @InjectedResource
    private BufferedImage backgroundGradient;
    @InjectedResource
    private float titleAlpha;
    @InjectedResource
    private BufferedImage title;
    @InjectedResource
    private Font pathFont;
    @InjectedResource
    private Color pathColor;
    @InjectedResource
    private float pathShadowOpacity;
    @InjectedResource
    private Color pathShadowColor;
    @InjectedResource
    private int pathShadowDistance;
    @InjectedResource
    private int pathShadowDirection;
    @InjectedResource
    private BufferedImage pathSeparatorLeft;
    @InjectedResource
    private BufferedImage pathSeparatorRight;
    @InjectedResource
    private BufferedImage haloPicture;

    Workspace workSpace;
    /////////////////////////////////////////////////////////////////////////

    public NavigationHeader(Workspace workSpace) {
      this.workSpace =workSpace;
        // Recuperation des valeurs depuis le fichier ressource
        ResourceInjector.get().inject(this);
        // On ajoute les elements a la suite -> FlowLayout
        setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        // On impose un petit decalage
        add(Box.createRigidArea(new Dimension(2, 2)));
        // On cree le gestionnaire d'evennement et la pile de boutton
        this.eventHandler = new PathButtonHandler();
        this.buttonStack = new Stack<PathButton>();
        this.addMouseListener(new MouseListener() {

        public void mouseClicked(MouseEvent e) {
              if(e.getPoint().getX()> NavigationHeader.this.getWidth()-150)
                if (TransitionManager.isToolBarDisplay())
                  TransitionManager.hideToolBar();
                else
                  TransitionManager.showToolBar();
        }
        public void mousePressed(MouseEvent e) {}
        public void mouseReleased(MouseEvent e) {}
        public void mouseEntered(MouseEvent e) {}
        public void mouseExited(MouseEvent e) {}
        });
    }

    /////////////////////////////////////////////////////////////////////////

    // Ajoute un boutton
    public void addLink(final String title) {
        // Si il y a deja des elements, on active le click at le highlight
        // pour l'avant dernier -> permettre retour
        if (buttonStack.size() > 0) {
            PathButton button = buttonStack.get(buttonStack.size() - 1);
            button.setHyperlinkCursor(true);
        }
        // On creer un nouveau boutton
        PathButton pathButton = new PathButton(title);
        // On l'ajoute à la pile
        add(buttonStack.push(pathButton));
        //
        revalidate();
        // On redessine
        repaint();
        LastBttn = title;
    }

    // Efface tous les boutton
    public void clearLinks() {
        // On vide toutes la pile
        while (buttonStack.size() > 0) 
            remove(buttonStack.pop());        
        //
        revalidate();
        // On reaffiche
        repaint();
        LastBttn = null;
    }

    // supprime tous les bouttons jusqu'a celui passe en parametre
    private void removeLinksAbove(final PathButton pathButton) {
        // tant que l'on a pas atteint le boutton desiré, on vide
        while (!buttonStack.peek().equals(pathButton)) {
            remove(buttonStack.pop());
        }
        // On rend le dernier boutton inactif ( inclickable, sans highlight)
        if (buttonStack.size() > 0) {
            PathButton button = buttonStack.get(buttonStack.size() - 1);
            button.setHyperlinkCursor(false);
        }
        //
        revalidate();
        // On redessine
        repaint();
    }

    @Override
    // On impose la hauteur
    public Dimension getPreferredSize() {
        Dimension size = super.getPreferredSize();
        size.height = preferredHeight;
        return size;
    }
    @Override
    public Dimension getMaximumSize() {
        Dimension size = super.getMaximumSize();
        size.height = preferredHeight;
        return size;
    }
   
    @Override
    // dessin
    protected void paintComponent(Graphics g) {
        // Si on est pas visible, on quitte
        if (!isVisible()) 
            return;
        // On recupere le graphics
        Graphics2D g2 = (Graphics2D) g;
        // On le configure
        setupGraphics(g2);
        // On dessine le fond
        paintBackground(g2);
        // On dessine le logo
        paintLogo(g2);
    }

    // Configure l'affichage
    private static void setupGraphics(final Graphics2D g2) {
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                            RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    }

    // Ecrit le nom en haut a droite
    private void paintLogo(final Graphics2D g2) {
        // On definit la transparence
        Composite composite = g2.getComposite();
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, titleAlpha));
        // On dessine le text
        g2.drawImage(title, getWidth() - title.getWidth(), 0, null);
        // On applique la transparence
        g2.setComposite(composite);
    }

    // Colore le fond
    private void paintBackground(final Graphics2D g2) {
        //On recupere la position en y 
        int height = backgroundGradient.getHeight();
        // Zonz utile
        Rectangle bounds = g2.getClipBounds();
        // On color le fond
        g2.drawImage(backgroundGradient,
                     (int) bounds.getX(), 0,
                     (int) bounds.getWidth(), height,
                     null);
        // On delimite la zone avec des traits
        g2.setColor(lightColor);
        g2.drawLine(0, height, getWidth(), height);
        g2.setColor(shadowColor);
        g2.drawLine(0, height + 1, getWidth(), height + 1);
    }

  public String getLastBttn() {
    return LastBttn;
  }

  public void setLastBttn(String LastBttn) {
    this.LastBttn = LastBttn;
  }

    /////////////////////////////////////////////////////////////////////////

    // Boutton de la barre de navigation
    private class PathButton extends JButton {
      
      /////////////////////////////////////////////////////////////////////////

        // Decalage de l'ombre sous le texte
        private final float shadowOffsetX;
        private final float shadowOffsetY;
        // Largeur du text
        private int textWidth;
        // Zone clickable
        private Rectangle clickable;
        // Valeur de l'effet
        private float ghostValue = 0.0f;

        /////////////////////////////////////////////////////////////////////////

        // Boutton portant le nom item 
        private PathButton(final String item) {
            super(item);

            setFont(pathFont);
            setFocusable(false);
            // pas de bord au boutton
            setBorderPainted(false);
            // Pas de fond
            setContentAreaFilled(false);
            // Pas de focus
            setFocusPainted(false);
            // Pas de marge
            setMargin(new Insets(0, 0, 0, 0));
            // Lageur necessaire a l'affichage du text
            FontMetrics metrics = getFontMetrics(pathFont);
            textWidth = SwingUtilities.computeStringWidth(metrics, getText());
            // Calcul des projections de l'ombre
            double rads = Math.toRadians(pathShadowDirection);
            shadowOffsetX = (float) Math.cos(rads) * pathShadowDistance;
            shadowOffsetY = (float) Math.sin(rads) * pathShadowDistance;
            // ajout du gestionnaire de click
            addMouseListener(eventHandler);
            // ajout du highlight au survol
            addMouseListener(new HiglightHandler());
        }

        /////////////////////////////////////////////////////////////////////////

        // defini le boutton comme cliquable ou non
        private void setHyperlinkCursor(boolean hyperlink) {
            // Si il est clickable
            if (hyperlink) {
                // On recupere la taille du texte pour definir la
                // zone clickable
                FontMetrics metrics = getFontMetrics(pathFont);
                int textHeight = metrics.getHeight();
                // 
                int x = 10;
                // Si on est pas le premier boutton
                if (this != buttonStack.get(0)) 
                    // On ajoute la largeur d'un séprateur
                    x += pathSeparatorRight.getWidth();
                // On creer la nouvelle zone clickable
                clickable = new Rectangle(x, metrics.getDescent(),textWidth, textHeight);
                // On ajoute l'hyperlink  au handler
                HyperlinkHandler.add(this, clickable);
            } else {
                // Si on est pas clickable, on supprime,l'hyperlink de l'handler
                HyperlinkHandler.remove(this);
            }
        }

        // On impose la hauteur
        @Override
        public Dimension getSize() {return getPreferredSize();}
        @Override
        public Dimension getPreferredSize() {
            int width = 20 + textWidth;
            if (this != buttonStack.peek())
                width += pathSeparatorLeft.getWidth();
            if (this != buttonStack.get(0)) 
                width += pathSeparatorRight.getWidth();
            return new Dimension(width, preferredHeight);
        }

        @Override
        // On dessine a la main
        protected void paintComponent(Graphics g) {
            // graphics
            Graphics2D g2 = (Graphics2D) g;
            // 
            Composite composite = g2.getComposite();
            // Si un effet est en cours
            if (ghostValue > 0.0f && this != buttonStack.peek()) {
                int x = -5;
                if (this != buttonStack.peek()) {
                    x += pathSeparatorLeft.getWidth();
                }
                // On configure l'affichage
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                                                           ghostValue));
                // On dessine le highlight
                g2.drawImage(haloPicture,
                             x, 0,
                             textWidth + 20, getHeight(), null);
            }

            float offset = 10.0f;
            if (this != buttonStack.get(0)) {
                offset += pathSeparatorRight.getWidth();
            }
            // On creer l'objet pour afficher le texte
            FontRenderContext context = g2.getFontRenderContext();
            TextLayout layout = new TextLayout(getText(), pathFont, context);

            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                                                       pathShadowOpacity));
            // On ajoute une petite ombre
            g2.setColor(pathShadowColor);
            layout.draw(g2,
                        shadowOffsetX + offset,
                        layout.getAscent() + layout.getDescent() + shadowOffsetY);
            g2.setComposite(composite);
            // On ecrit le texte
            g2.setColor(pathColor);
            layout.draw(g2,
                        offset, layout.getAscent() + layout.getDescent());
            // Si onest pas le dernier boutton, on creer un separateur à gauche
            if (this != buttonStack.peek()) {
                g2.drawImage(pathSeparatorLeft,
                             getWidth() - pathSeparatorLeft.getWidth(), 0,
                             null);
            }
            // Si on est pas le premier on creer un separateur a droite
            if (this != buttonStack.get(0)) {
                g2.drawImage(pathSeparatorRight, 0, 0, null);
            }
        }

        /////////////////////////////////////////////////////////////////////////

        // gere le highlight au survol
        private final class HiglightHandler extends MouseAdapter {
          
          /////////////////////////////////////////////////////////////////////////

            // Pour timer les fade-in/out
            private Animator timer;
            
            /////////////////////////////////////////////////////////////////////////

            @Override
            // Souris sur le boutton
            public void mouseEntered(MouseEvent e) {
                // si une animation est en cours, on l'arrete
                if (timer != null && timer.isRunning())
                    timer.stop();
                // On lance une nouvelle animation de fade-in
                timer = new Animator(300, new AnimateGhost(true));
                timer.start();
            }

            @Override
            public void mouseExited(MouseEvent e) {
                // si une animation est en cours, on l'arrete
                if (timer != null && timer.isRunning())
                    timer.stop();
               // On lance une nouvelle animation de fade-in
                timer = new Animator(300, new AnimateGhost(false));
                timer.start();
            }
        }

        /////////////////////////////////////////////////////////////////////////

        // Effectue le fade-in, fade-out
        private final class AnimateGhost implements TimingTarget {
          
          /////////////////////////////////////////////////////////////////////////

            // sens de l'effet
            private boolean forward;
            // etat precedent
            private float oldValue;

            /////////////////////////////////////////////////////////////////////////

            // Calcul la valeur de l'eclairage pour le fad in/out
            AnimateGhost(boolean forward) {
                // Initilisation du sens
                this.forward = forward;
                // On recupere la valeur de l'eclairage d'origine
                oldValue = ghostValue;
            }

            /////////////////////////////////////////////////////////////////////////

            public void repeat() {}
            
            public void timingEvent(float fraction) {
                // On calcule la noouveau facteur de lumiere
                ghostValue = oldValue + fraction * (forward ? 1.0f : -1.0f);
                // On limite netre 0 et 1
                if (ghostValue > 1.0f) 
                    ghostValue = 1.0f;
                else if (ghostValue < 0.0f)
                    ghostValue = 0.0f;
                // On redessine
                repaint();
            }

            public void begin() {}
            public void end() {}
        }
    }

    /////////////////////////////////////////////////////////////////////////

    // Gere les clics sur les bouttons
    private class PathButtonHandler extends MouseAdapter {
        @Override
        // CLick
        public void mouseClicked(MouseEvent e) {
            // On recupere le boutton originaire de l'event
            PathButton pathButton = (PathButton) e.getSource();
            // Index de l'emetteur dans laliste
            int index = buttonStack.indexOf(pathButton);
            // Si on a clicke sur le dernier boutton ou hors de
            // la zone clickable, on quitte
            if (index == buttonStack.size() - 1 ||
                !pathButton.clickable.contains(e.getPoint())){
                    return;
            }
            // Selon l'index de l'emmeteur
            switch (index) {
                // Cas Accueil
                case 0:
                    // On retourne a l'acceuil
                    TransitionManager.showMainScreen();
                    //removeLinksAbove(pathButton);
                    break;
                // Cas project
                case 1:
                    // On va a la page projet
                    TransitionManager.showMainPoject();
                    // On met à jour la barre de navigation
                    //removeLinksAbove(pathButton);
                    break;
                                     
                default:
                    if (pathButton.getText().equals("Project"))
                      TransitionManager.showMainPoject();
                    else if(pathButton.getText().equals("Editeur"))
                      TransitionManager.showProjectEdit();
                    else{
                        for (Task t : workSpace.getCurrentTask().getTaskList(true, true)) {
                          if (t.getName().equals(pathButton.getText())){
                            workSpace.setCurrentTask(t);
                            TransitionManager.showEdit(); 
                          }
                        }
                    }
                    break;
            }
        }
    }
    
    /////////////////////////////////////////////////////////////////////////
    
}

/////////////////////////////////////////////////////////////////////////
